
//  UserAccountViewModel.swift
//如果碰到控制器要处理的业务逻辑非常非常多 可以建立多个视图模型，分担同一个控制器的工作
//视图模型只用来封装用户账号的
//用户账号视图模型 - 没有父类
/*
模型通常继承自NSObject -> 可以使用 KVC 设置属性，简化对象构造
如果没有父类，所有的内容，都需要从头创建，量级更轻

视图模型的作用：封装‘业务逻辑’ 通常没有复杂的属性
越重要的属性往上面写
*/

import Foundation
class UserAccountViewModel {
    
 /// 单例 - 解决避免重复从沙盒加载 归档文件，提高效率，让 access_token 便于被访问到
    static let sharedUserAccount = UserAccountViewModel() //这个括号调用init构造函数
   
    /// 用户模型
    var account:UserAccount?
    
 /// 返回有效的token
    var accessToken: String?{
        
        // 如果 token 没有没有过期，返回 account中的token属性  计算型属性
        if !isExpired{
            return account?.access_token
        }
        return nil
    }

 /// 用户登录标记  计算型属性
    var userLogon: Bool{
        
        //1.如果 token 有值，说明登录成功
        //2.如果没有过期，说明登录有效
        return account?.access_token != nil && !isExpired
    }
    
 /// 用户头像URL
    var avatarUrl: NSURL{
        return NSURL(string: account?.avatar_large ?? "")! //做强行解包 就保证返回的是NSURL  NSURL是可选  avatar_large是必选
    
    }
    
    /// 计算型属性（类似于有返回值的函数 可以让调用的时候 语意更加清晰） -
    //归档保存的路径
    //如果在OC中，可以通过只读属性/函数的方式实现
    private var accountPath: String{
        
    let path = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true).last!  //last可选的  强行解包
        
    return (path as NSString).stringByAppendingPathComponent("account.plist")
        
}

///    判断账户是否过期  
    private var isExpired: Bool{
        
        //判断用户账户过期日期与当前系统日期“进行比较”
        //自己改写日期，测试逻辑是否正确，创建日期的时候，如果给定 负数，返回比当前时间早得日期
//        account?.expiresDate = NSDate(timeIntervalSinceNow: -3600)
        
        //如果 account 为nil，不会调用后面的属性，后面的比较也不会继续...
        //第一个？ 账户存在执行后面的 第二个？ 账户过期执行后面的
        if account?.expiresDate?.compare(NSDate()) == NSComparisonResult.OrderedDescending{
            
            //代码执行到此，一定进行过比较
            return false
        }
        
        //如果过期返回 true
        
        return true
    }
    
//构造函数 这里没有overrid 因为没有父类 不涉及重写和继承
//私有化 - 会要求外部只能通过单例常量访问，而不能 () 实例化
   private init() {
        
        //从沙盒解档数据，恢复当前数据 - 磁盘读写的速度最慢，不如内存读写效率高
        //解档就是恢复当前数据就 构造函数中解档完就是给属性赋值
        //从沙盒解档数据，恢复当前数据
         account = NSKeyedUnarchiver.unarchiveObjectWithFile(accountPath) as? UserAccount
        
        //判断token是否过期  用属性判断
        if isExpired{
        
        print("已经过期")
        
       //如果过期，清空解档的数据
            account = nil
        }
        
//        print(account)
    }
}


//MARK:-用户账户相关的网络方法
/*
  代码重构的步骤
  1.新方法
  2.粘贴代码
  3.根据上下文调整参数和返回值
  4.移动其他‘子’方法
*/

extension UserAccountViewModel{
    

    func loadAccessToken(code: String, finished: (isSuccessd: Bool)->()) {
        
        //4.加载 accessToken
        NetworkTools.sharedTools.loadAccessToken(code){(result, error) -> ()in
        
        //1>判断错误
        if error != nil {
            print("出错了")
            
            //失败的回调
            finished(isSuccessd: false)

            return
        }
        
        //2>输出结果 - 在swift中任何 AnyObject 在使用前必须转换类型-> as ?/! 类型
            
        //创建账户对象 - 保存在self.account 属性中
     self.account = UserAccount(dict: result as! [String: AnyObject])
        //再次发起网络请求拿到用户的账户信息并保存
     self.loadUserInfo(self.account!,finished: finished)
        
        }
    }
    
/// 加载用户信息
///
/// - parameter account:  用户账户对象
/// - parameter finished: 完成的回调(是否成功)
private func loadUserInfo(account: UserAccount, finished: (isSuccessed: Bool)->()) {
    
    NetworkTools.sharedTools.loadUserInfo(account.uid!){(result, error)-> () in
        
        if error != nil {
            
            print("加载用户出错了")
            
            //失败的回调
            finished(isSuccessed: false)
            return
        }
        
        //提示：如果使用 if let / guard let 统统使用 ‘?’
        //做了两个判断
        //1.result 一定有内容
        //2.一定是字典
        guard let dict = result as? [String: AnyObject] else{
            
            print("格式错误")
            
            finished(isSuccessed: false)
            return
        }
        
        //dict 一定是一个有值的字典
        //将用户信息保存    AnyObject在使用的时候一定要做类型转换
        account.screen_name = dict["screen_name"] as? String
        account.avatar_large = dict["avatar_large"] as? String
      
        
        //保存对象 - 会调用对象的 encodeWithCode 方法 外部调用archiveRootObject归档的对象内部就会调用内部的方法实现保存的过程
//        account.saveUserAccount()
        NSKeyedArchiver.archiveRootObject(account, toFile: self.accountPath)
        print(self.accountPath)
        
        //需要完成回调  完成的回调必须要写不然调用方不知道结果一直在等
        finished(isSuccessed: true)
    }
    }
}















